#include  "TerrainManager.h"

DetailsManager::DetailsManager( TerrainManager *_term, PlanetaryConfig *_CFG ) {

	term = _term;
	CFG = _CFG;

	GS_Average = GS_High = GS_City = NULL;
	PS_Average = PS_High = PS_City = NULL;
	aTextures_AverageHigh = NULL;
	VB_AverageHigh = NULL;
}

DetailsManager::~DetailsManager() {
	//vegetation:
	REL( aTextures_AverageHigh );
	REL( GS_Average );
	REL( PS_Average );
	REL( GS_High );
	REL( PS_High );

	//city:
	for( int j = 0; j < aMESH.size(); j++ ) {
		REL( aMESH[j].VB );
		REL( aMESH[j].aTextures );
	}
	aMESH.clear();
	REL( GS_City );
	REL( PS_City );
}

UINT
	DetailsManager::Stride_POINTVERTEX = 52,
	DetailsManager::Stride_TRIANGLEVERTEX = 48,
	DetailsManager::ntile_avg = 0,
	DetailsManager::size_avg = 0,
	DetailsManager::ntile_high = 0,
	DetailsManager::size_high = 0,
	DetailsManager::ntile_city = 0,
	DetailsManager::size_city = 0;
TILERENDERSPEC
	*DetailsManager::TRS_AVG = NULL,
	*DetailsManager::TRS_HIGH = NULL,
	*DetailsManager::TRS_CITY = NULL;
ID3D11InputLayout
	*DetailsManager::IL_POINTVERTEX = NULL,
	*DetailsManager::IL_TRIANGLEVERTEX = NULL;
ID3D11VertexShader
	*DetailsManager::VS_AverageHigh = NULL,
	*DetailsManager::VS_City = NULL;
DetailsManager::RENDERPARAM
	DetailsManager::RP;
ID3D11Buffer
	*DetailsManager::VB_AverageHigh = NULL,
	*DetailsManager::VSCB_Tile = NULL,
	*DetailsManager::CB_AtmParams = NULL,
	*DetailsManager::CB_PlanetShadowParams = NULL,
	*DetailsManager::CB_CoverTypeParams = NULL;
ID3D11DepthStencilState
	*DetailsManager::DSS_Tile = NULL;
ID3D11SamplerState
	*DetailsManager::SS_HeightMap = NULL,			//<= TileRenderMgr
	*DetailsManager::SS_TileTexture = NULL,			//<= TileRenderMgr
	*DetailsManager::SS_TextureAverageHigh = NULL,
	*DetailsManager::SS_TextureCity = NULL;

void DetailsManager::GlobalInit( ID3D11Buffer *_CB_AtmParams, ID3D11Buffer *_CB_EclipseParams ) {

	CB_AtmParams = _CB_AtmParams;
	//_CB_EclipseParams

	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	//average and high detalization vegetation/rocks:
	D3D11_INPUT_ELEMENT_DESC iedesc[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 0, 44, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 2, DXGI_FORMAT_R32_FLOAT, 0, 48, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_AverageHigh.fx", NULL, NULL, "VS_AverageHigh", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateInputLayout( iedesc, 6, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_POINTVERTEX ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_AverageHigh ) );
	REL( SBlob );
	REL( EBlob );

	//cities:
	D3D11_INPUT_ELEMENT_DESC iedesc_city[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "POSITION", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "POSITION", 2, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 1, DXGI_FORMAT_R32_UINT, 0, 44, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_City.fx", NULL, NULL, "VS_City", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateInputLayout( iedesc_city, 5, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_TRIANGLEVERTEX ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_City ) );
	REL( SBlob );
	REL( EBlob );

	D3D11_BUFFER_DESC bdesc;
	D3D11_SUBRESOURCE_DATA sdata;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	//=======================================================
	//		Vertex buffer of randomly positioned points
	//=======================================================

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_SHADER_RESOURCE;
	bdesc.ByteWidth = Stride_POINTVERTEX*NPOINT;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	bdesc.StructureByteStride = Stride_POINTVERTEX;
	bdesc.Usage = D3D11_USAGE_DEFAULT;
	
	double
		lat, lng,
		maxlat = PI05/512.0,
		maxlng = PI2/2048.0;
	POINTVERTEX vtx, *VTX = new POINTVERTEX [NPOINT];
	for( UINT j = 0; j < NPOINT; j++ ) {
		lat = maxlat*(double(rand())/double(RAND_MAX));
		lng = maxlng*(double(rand())/double(RAND_MAX));
		vtx.PosL.x = 1000.0*cos(lng)*cos(lat);
		vtx.PosL.y = 1000.0*sin(lat);
		vtx.PosL.z = 1000.0*sin(lng)*cos(lat);
		D3DXVec3Normalize( &vtx.NormL, &vtx.PosL );
		vtx.TangL.x = -1.0*sin(lng)*cos(lat);
		vtx.TangL.y = cos(lat);
		vtx.TangL.z = cos(lng)*cos(lat);
		vtx.TCrd = D3DXVECTOR2( lng/maxlng, lat/maxlat );
		vtx.dist = 0.0;//random ?
		vtx.rnd = double(rand())/double(RAND_MAX);
		VTX[j] = vtx;
	}

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = VTX;

	Dev->CreateBuffer( &bdesc, &sdata, &VB_AverageHigh );
	delete [ ] VTX;

	//constant buffers
	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 96;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	Dev->CreateBuffer( &bdesc, &sdata, &VSCB_Tile );

	//TILERENDERSPEC buffers
	TRS_AVG = new TILERENDERSPEC [32];
	size_avg = 32;

	TRS_HIGH = new TILERENDERSPEC [32];
	size_high = 32;

	TRS_CITY = new TILERENDERSPEC [32];
	size_city = 32;

	//samplers:
	SS_HeightMap = TileRenderMgr::SS_HeightMap;
	SS_TileTexture = TileRenderMgr::SS_TileTexture;

	D3D11_SAMPLER_DESC sdesc;

	//average and high detalization textures
	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	Dev->CreateSamplerState( &sdesc, &SS_TextureAverageHigh );

	//building textures
	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	Dev->CreateSamplerState( &sdesc, &SS_TextureCity );

	D3D11_DEPTH_STENCIL_DESC ddesc;

	//depth-stencil state with enabled depth
	memset( &ddesc, 0, sizeof(ddesc) );
	ddesc.DepthEnable = true;
	ddesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
	ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	ddesc.StencilEnable = false;
	ddesc.StencilReadMask = 0;
	ddesc.StencilWriteMask = 0;

	Dev->CreateDepthStencilState( &ddesc, &DSS_Tile );
}

void DetailsManager::GlobalExit() {
	REL( IL_POINTVERTEX );
	REL( IL_TRIANGLEVERTEX );

	REL( VSCB_Tile );
	REL( VB_AverageHigh );
	REL( VS_AverageHigh );
	REL( VS_City );

	delete [ ] TRS_AVG;
	delete [ ] TRS_HIGH;
	delete [ ] TRS_CITY;

	REL( SS_TextureAverageHigh );
	REL( SS_TextureCity );
}

void DetailsManager::Render( TILERENDERSPEC *TRS, UINT ntile ) {
	if( !CFG->bTerrainDetails_Average )	return;

	//create list of average, high and city tiles
	bool south = false;
	RP.cpos = term->RP.cpos;
	RP.objsize = term->RP.objsize;
	RP.mWorld_tmp = RP.mWorld = term->mWorld_north;
	RP.grot = term->grot_north;
	for( int j = 0; j < ntile; j++ ) {
		if( TRS[j].hemisphere && !south ) {
			D3DXMatrixMultiply( &RP.mWorld, &term->RSouth, &RP.mWorld );
			RP.mWorld_tmp = RP.mWorld;
			RP.grot.m12 = -RP.grot.m12;
			RP.grot.m13 = -RP.grot.m13;
			RP.grot.m22 = -RP.grot.m22;
			RP.grot.m23 = -RP.grot.m23;
			RP.grot.m32 = -RP.grot.m32;
			RP.grot.m33 = -RP.grot.m33;
			south = true;
		}
		ProcessTile(	TRS[j].level, TRS[j].hemisphere,
						TRS[j].ilat, TRS[j].nlat, TRS[j].ilng, TRS[j].nlng, TRS[j].td,
						TRS[j].RANGE, TRS[j].Tex, TRS[j].RANGE, TRS[j].Tex );
	}

	const UINT VBOffset = 0;
	iCtx->IASetInputLayout( IL_POINTVERTEX );
	iCtx->IASetVertexBuffers( 0, 1, &VB_AverageHigh, &Stride_POINTVERTEX, &VBOffset );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );
	iCtx->VSSetShader( VS_AverageHigh, NULL, 0 );
	iCtx->VSSetSamplers( 0, 1, &SS_HeightMap );
	iCtx->VSSetConstantBuffers( 0, 1, &VSCB_Tile );
	iCtx->RSSetState( RS_CullNone_Solid );
	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_Tile, 0 );
	
	//==================================================================================
	//									Average
	//==================================================================================

	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, SC->GetVP(), 0, 0 );
	iCtx->GSSetShader( GS_Average, NULL, 0 );
	iCtx->GSSetConstantBuffers( 0, 1, &cb_D3DXMATRIX_x1 );
	iCtx->GSSetConstantBuffers( 1, 1, &CB_CoverTypeParams );	//texture idx etc. data <= TileRenderMgr
	iCtx->GSSetSamplers( 0, 1, &SS_TileTexture );

	iCtx->PSSetShader( PS_Average, NULL, 0 );
	//iCtx->PSSetConstantBuffers( 0, 1, &cb_PSCB_per_frame );
	iCtx->PSSetConstantBuffers( 1, 1, &CB_CoverTypeParams );
	iCtx->PSSetShaderResources( 0, 1, &aTextures_AverageHigh );
	iCtx->PSSetSamplers( 0, 1, &SS_TextureAverageHigh );

	VSCB_per_tile vbuf;
	for( int j = 0; j < ntile_avg; j++ ) {

		vbuf.W = TRS_AVG[j].mWorld;
		vbuf.TexOff[0] = TRS_AVG[j].RANGE.range_tex.tumax - TRS_AVG[j].RANGE.range_tex.tumin;
		vbuf.TexOff[1] = TRS_AVG[j].RANGE.range_tex.tumin;
		vbuf.TexOff[2] = TRS_AVG[j].RANGE.range_tex.tvmax - TRS_AVG[j].RANGE.range_tex.tvmin;
		vbuf.TexOff[3] = TRS_AVG[j].RANGE.range_tex.tvmin;
		vbuf.TexOff_height[0] = TRS_AVG[j].RANGE.range_height.tumax - TRS_AVG[j].RANGE.range_height.tumin;
		vbuf.TexOff_height[1] = TRS_AVG[j].RANGE.range_height.tumin;
		vbuf.TexOff_height[2] = TRS_AVG[j].RANGE.range_height.tvmax - TRS_AVG[j].RANGE.range_height.tvmin;
		vbuf.TexOff_height[3] = TRS_AVG[j].RANGE.range_height.tvmin;
		vbuf.TexOff_lcover[0] = TRS_AVG[j].RANGE.range_lcover.tumax - TRS_AVG[j].RANGE.range_lcover.tumin;
		vbuf.TexOff_lcover[1] = TRS_AVG[j].RANGE.range_lcover.tumin;
		vbuf.TexOff_lcover[2] = TRS_AVG[j].RANGE.range_lcover.tvmax - TRS_AVG[j].RANGE.range_lcover.tvmin;
		vbuf.TexOff_lcover[3] = TRS_AVG[j].RANGE.range_lcover.tvmin;
		iCtx->UpdateSubresource( VSCB_Tile, 0, NULL, &vbuf, 0, 0 );
		iCtx->VSSetShaderResources( 0, 1, TRS_AVG[j].Tex.htex->GetTexture() );

		iCtx->GSSetShaderResources( 0, 1, TRS_AVG[j].Tex.tex->GetTexture() );
		iCtx->GSSetShaderResources( 1, 1, &TRS_AVG[j].Tex.ctex );

		iCtx->Draw( NPOINT, 0 );
	}

	//==================================================================================
	//									High
	//==================================================================================

	if( !CFG->bTerrainDetails_High ) {
		iCtx->GSSetShader( NULL, NULL, 0 );
		return;
	}

	iCtx->GSSetShader( GS_High, NULL, 0 );
	iCtx->PSSetShader( PS_High, NULL, 0 );

	for( int j = 0; j < ntile_high; j++ ) {

		vbuf.W = TRS_HIGH[j].mWorld;
		vbuf.TexOff[0] = TRS_HIGH[j].RANGE.range_tex.tumax - TRS_HIGH[j].RANGE.range_tex.tumin;
		vbuf.TexOff[1] = TRS_HIGH[j].RANGE.range_tex.tumin;
		vbuf.TexOff[2] = TRS_HIGH[j].RANGE.range_tex.tvmax - TRS_HIGH[j].RANGE.range_tex.tvmin;
		vbuf.TexOff[3] = TRS_HIGH[j].RANGE.range_tex.tvmin;
		vbuf.TexOff_height[0] = TRS_HIGH[j].RANGE.range_height.tumax - TRS_HIGH[j].RANGE.range_height.tumin;
		vbuf.TexOff_height[1] = TRS_HIGH[j].RANGE.range_height.tumin;
		vbuf.TexOff_height[2] = TRS_HIGH[j].RANGE.range_height.tvmax - TRS_HIGH[j].RANGE.range_height.tvmin;
		vbuf.TexOff_height[3] = TRS_HIGH[j].RANGE.range_height.tvmin;
		vbuf.TexOff_lcover[0] = TRS_HIGH[j].RANGE.range_lcover.tumax - TRS_HIGH[j].RANGE.range_lcover.tumin;
		vbuf.TexOff_lcover[1] = TRS_HIGH[j].RANGE.range_lcover.tumin;
		vbuf.TexOff_lcover[2] = TRS_HIGH[j].RANGE.range_lcover.tvmax - TRS_HIGH[j].RANGE.range_lcover.tvmin;
		vbuf.TexOff_lcover[3] = TRS_HIGH[j].RANGE.range_lcover.tvmin;
		iCtx->UpdateSubresource( VSCB_Tile, 0, NULL, &vbuf, 0, 0 );
		iCtx->VSSetShaderResources( 0, 1, TRS_HIGH[j].Tex.htex->GetTexture() );

		iCtx->GSSetShaderResources( 0, 1, TRS_HIGH[j].Tex.tex->GetTexture() );
		iCtx->GSSetShaderResources( 1, 1, &TRS_HIGH[j].Tex.ctex );

		iCtx->Draw( NPOINT, 0 );
	}

	//==================================================================================
	//									Urban
	//==================================================================================

	if( !ntile_city ) {
		iCtx->GSSetShader( NULL, NULL, 0 );
		return;
	}

	//urban zones
	//pseudo-random value define idx of mesh:
	UINT nmesh = aMESH.size();
	iCtx->IASetInputLayout( IL_TRIANGLEVERTEX );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );
	iCtx->VSSetShader( VS_City, NULL, 0 );
	iCtx->GSSetShader( GS_City, NULL, 0 );
	iCtx->PSSetShader( PS_City, NULL, 0 );
	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );

	for( int j = 0; j < ntile_city; j++ ) {

		srand( TRS_CITY[j].ilat + TRS_CITY[j].ilng );
		UINT idx = rand() % nmesh;

		iCtx->IASetVertexBuffers( 0, 1, &aMESH[idx].VB, &Stride_TRIANGLEVERTEX, &VBOffset );

		vbuf.W = TRS_CITY[j].mWorld;
		vbuf.TexOff[0] = TRS_CITY[j].RANGE.range_tex.tumax - TRS_CITY[j].RANGE.range_tex.tumin;
		vbuf.TexOff[1] = TRS_CITY[j].RANGE.range_tex.tumin;
		vbuf.TexOff[2] = TRS_CITY[j].RANGE.range_tex.tvmax - TRS_CITY[j].RANGE.range_tex.tvmin;
		vbuf.TexOff[3] = TRS_CITY[j].RANGE.range_tex.tvmin;
		vbuf.TexOff_height[0] = TRS_CITY[j].RANGE.range_height.tumax - TRS_CITY[j].RANGE.range_height.tumin;
		vbuf.TexOff_height[1] = TRS_CITY[j].RANGE.range_height.tumin;
		vbuf.TexOff_height[2] = TRS_CITY[j].RANGE.range_height.tvmax - TRS_CITY[j].RANGE.range_height.tvmin;
		vbuf.TexOff_height[3] = TRS_CITY[j].RANGE.range_height.tvmin;
		vbuf.TexOff_lcover[0] = TRS_CITY[j].RANGE.range_lcover.tumax - TRS_CITY[j].RANGE.range_lcover.tumin;
		vbuf.TexOff_lcover[1] = TRS_CITY[j].RANGE.range_lcover.tumin;
		vbuf.TexOff_lcover[2] = TRS_CITY[j].RANGE.range_lcover.tvmax - TRS_CITY[j].RANGE.range_lcover.tvmin;
		vbuf.TexOff_lcover[3] = TRS_CITY[j].RANGE.range_lcover.tvmin;
		iCtx->UpdateSubresource( VSCB_Tile, 0, NULL, &vbuf, 0, 0 );
		iCtx->VSSetShaderResources( 0, 1, TRS_CITY[j].Tex.htex->GetTexture() );

		iCtx->GSSetShaderResources( 0, 1, TRS_CITY[j].Tex.tex->GetTexture() );
		iCtx->GSSetShaderResources( 1, 1, &TRS_CITY[j].Tex.ctex );

		iCtx->Draw( aMESH[idx].nvtx, aMESH[idx].svtx );
	}

	iCtx->GSSetShader( NULL, NULL, 0 );
}

void DetailsManager::ProcessTile( UINT8 level, UINT8 hemisphere, UINT ilat, UINT nlat, UINT ilng, UINT nlng, TILEDESC1 *td, TILECRDRANGE RANGE, TILETEX Tex, TILECRDRANGE kbp_RANGE, TILETEX bkp_Tex ) {
	
	bool bStepDown = level < DETAILS_TILELVL;

	D3DXVECTOR3 bpos;
	D3DXMATRIX mWorld;

	TileWorldMatrix( hemisphere, ilat, nlat, ilng, nlng, mWorld );

	D3DXVec3TransformCoord( &bpos, &td->bsPos, &mWorld );
	float dist = D3DXVec3Length( &bpos );
	float min_dist = (dist > td->bsRad ? dist - td->bsRad : 0.0f);
	float max_dist = dist + td->bsRad;

	if( min_dist > Details_maxdist )	return;//urban > average > high

	if( bStepDown ) {

		UINT idx = 0;
		float du[3] = {
			(RANGE.range_tex.tumax - RANGE.range_tex.tumin)*0.5f,
			(RANGE.range_height.tumax - RANGE.range_height.tumin)*0.5f,
			(RANGE.range_lcover.tumax - RANGE.range_lcover.tumin)*0.5f
		};

		float dv[3] = {
			(RANGE.range_tex.tvmax - RANGE.range_tex.tvmin)*0.5f,
			(RANGE.range_height.tvmax - RANGE.range_height.tvmin)*0.5f,
			(RANGE.range_lcover.tvmax - RANGE.range_lcover.tvmin)*0.5f
		};

		TILECRDRANGE SUBRANGE;
		static TEXCRDRANGE1 fullrange = { 0.0f, 1.0f, 0.0f, 1.0f };
		for( int i = 1; i >= 0; i-- ) {

			SUBRANGE.range_tex.tvmax = (SUBRANGE.range_tex.tvmin = RANGE.range_tex.tvmin + (1-i)*dv[0]) + dv[0];
			SUBRANGE.range_height.tvmax = (SUBRANGE.range_height.tvmin = RANGE.range_height.tvmin + (1-i)*dv[1]) + dv[1];
			SUBRANGE.range_lcover.tvmax = (SUBRANGE.range_lcover.tvmin = RANGE.range_lcover.tvmin + (1-i)*dv[2]) + dv[2];

			for( int j = 0; j < 2; j++ ) {

				SUBRANGE.range_tex.tumax = (SUBRANGE.range_tex.tumin = RANGE.range_tex.tumin + j*du[0]) + du[0];
				SUBRANGE.range_height.tumax = (SUBRANGE.range_height.tumin = RANGE.range_height.tumin + j*du[1]) + du[1];
				SUBRANGE.range_lcover.tumax = (SUBRANGE.range_lcover.tumin = RANGE.range_lcover.tumin + j*du[2]) + du[2];

				bool bIsFull = true;
				TILEDESC1 *subtile = td->subtile[idx];
				if( !subtile )							bIsFull = false;
				else if( subtile->flags & LOAD_TEX )	bIsFull = false;

				TILECRDRANGE nRANGE, nSUBRANGE;
				TILETEX nTex, bkp_nTex;
				if( bIsFull )	bIsFull = (subtile->tex != NULL);

				//height map
				if( !(subtile->flags & LOAD_HEIGHT) && subtile->htex ) {
					bkp_nTex.htex = nTex.htex = subtile->htex.get();
					nSUBRANGE.range_height = nRANGE.range_height = fullrange;
				}
				else {
					bkp_nTex.htex = nTex.htex = Tex.htex;
					nSUBRANGE.range_height = nRANGE.range_height = SUBRANGE.range_height;
				}

				//land cover
				if( !(subtile->flags & LOAD_LCOVER) && subtile->ctex ) {
					bkp_nTex.ctex = nTex.ctex = subtile->ctex;
					nSUBRANGE.range_lcover = nRANGE.range_lcover = fullrange;
				}
				else {
					bkp_nTex.ctex = nTex.ctex = Tex.ctex;
					nSUBRANGE.range_lcover = nRANGE.range_lcover = SUBRANGE.range_lcover;
				}

				if( bIsFull ) {
					nTex.tex = subtile->tex.get();
					nTex.ltex = subtile->ltex;
					bkp_nTex.tex = Tex.tex;
					bkp_nTex.ltex = Tex.ltex;
					nRANGE.range_tex = fullrange;
					nSUBRANGE.range_tex = SUBRANGE.range_tex;
					ProcessTile( level+1, hemisphere, ilat*2+i, nlat*2, ilng*2+j, nlng*2, subtile, nRANGE, nTex, nSUBRANGE, bkp_nTex );
				}
				else {
					bkp_nTex.tex = nTex.tex = Tex.tex;
					bkp_nTex.ltex = nTex.ltex = Tex.ltex;
					nRANGE.range_tex = SUBRANGE.range_tex;
					nSUBRANGE.range_tex = SUBRANGE.range_tex;
					ProcessTile( level+1, hemisphere, ilat*2+i, nlat*2, ilng*2+j, nlng*2, subtile, nRANGE, nTex, nSUBRANGE, bkp_nTex );
				}
				idx++;
			}
		}
	}
	else {

		if( min_dist < CFG->TerrainDetails_Average_maxdist && CFG->TerrainDetails_High_maxdist < max_dist ) {
			if( ntile_avg == size_avg )		ExpandTRSBuffer( TRS_AVG, size_avg );

			TRS_AVG[ntile_avg].mWorld = mWorld;
			TRS_AVG[ntile_avg].ilat = ilat;
			TRS_AVG[ntile_avg].nlat = nlat;
			TRS_AVG[ntile_avg].ilng = ilng;
			TRS_AVG[ntile_avg].nlng = nlng;
			TRS_AVG[ntile_avg].hemisphere = hemisphere;
			TRS_AVG[ntile_avg].level = level;
			TRS_AVG[ntile_avg].RANGE = RANGE;
			TRS_AVG[ntile_avg].Tex = Tex;
			ntile_avg++;
		}

		if( CFG->bTerrainDetails_High && min_dist < CFG->TerrainDetails_High_maxdist ) {
			if( ntile_high == size_high )		ExpandTRSBuffer( TRS_HIGH, size_high );

			TRS_HIGH[ntile_high].mWorld = mWorld;
			TRS_HIGH[ntile_high].ilat = ilat;
			TRS_HIGH[ntile_high].nlat = nlat;
			TRS_HIGH[ntile_high].ilng = ilng;
			TRS_HIGH[ntile_high].nlng = nlng;
			TRS_HIGH[ntile_high].hemisphere = hemisphere;
			TRS_HIGH[ntile_high].level = level;
			TRS_HIGH[ntile_high].RANGE = RANGE;
			TRS_HIGH[ntile_high].Tex = Tex;
			ntile_high++;
		}

		if( CFG->bTerrainDetails_Urban && (td->flags & CITY_CTYPE) && min_dist < CFG->TerrainDetails_Urban_maxdist ) {
			if( ntile_city == size_city )		ExpandTRSBuffer( TRS_CITY, size_city );

			TRS_CITY[ntile_city].mWorld = mWorld;
			TRS_CITY[ntile_city].ilat = ilat;
			TRS_CITY[ntile_city].nlat = nlat;
			TRS_CITY[ntile_city].ilng = ilng;
			TRS_CITY[ntile_city].nlng = nlng;
			TRS_CITY[ntile_city].hemisphere = hemisphere;
			TRS_CITY[ntile_city].level = level;
			TRS_CITY[ntile_city].RANGE = RANGE;
			TRS_CITY[ntile_city].Tex = Tex;
			ntile_city++;
		}
	}
}

void DetailsManager::ExpandTRSBuffer( TILERENDERSPEC *TRS, UINT &size ) {
	TILERENDERSPEC *trs = new TILERENDERSPEC [size + 32];
	memcpy( trs, TRS, size );
	delete [ ] TRS;
	TRS = trs;
	size += 32;
}

void DetailsManager::TileWorldMatrix( UINT8 hemisphere, UINT ilat, UINT nlat, UINT ilng, UINT nlng, D3DXMATRIX &mWorld ) {
	//returns tile-specific world matrix
//	void MatrixRotY1( D3DXMATRIX *out, float angle );

	D3DXMATRIX rtile, wtrans;
	double lng = PI2*(double)ilng/(double)nlng + PI;

	MatrixRotY1( &rtile, (float)lng );

	if( nlat > 8 ) {
		double lat = PI05*(double)ilat/(double)nlat;
		double s = RP.objsize;
		double dx = s*cos(lng)*cos(lat);
		double dy = s*sin(lat);
		double dz = s*sin(lng)*cos(lat);
		RP.mWorld_tmp._41 = (float)(dx*RP.grot.m11 + dy*RP.grot.m12 + dz*RP.grot.m13 + RP.cpos.x);
		RP.mWorld_tmp._42 = (float)(dx*RP.grot.m21 + dy*RP.grot.m22 + dz*RP.grot.m23 + RP.cpos.y);
		RP.mWorld_tmp._43 = (float)(dx*RP.grot.m31 + dy*RP.grot.m32 + dz*RP.grot.m33 + RP.cpos.z);
		D3DXMatrixMultiply( &mWorld, &rtile, &RP.mWorld_tmp );
	}
	else
		D3DXMatrixMultiply( &mWorld, &rtile, &RP.mWorld );
}

/*
void MatrixRotY( D3DXMATRIX *out, float angle ) {
	double sinr = sin( angle ), cosr = cos( angle );
	memset( out, 0, 64 );
	out->_11 = out->_33 = (float)cosr;
	out->_31 = -( out->_13 = (float)sinr );
	out->_22 = out->_44 = 1.0f;
}*/

void DetailsManager::InitResources() {

	char fname[256], cpath[256];

	D3D10_SHADER_MACRO macro;
	std::vector<D3D10_SHADER_MACRO> SM;
	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	ID3D11Texture2D *tex2;
	D3D11_TEXTURE2D_DESC tdesc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;

	//=============================================
	//			Average/High vegetation/rocks
	//=============================================

	if( CFG->bTerrainDetails_Average ) {

		BYTE *data;
		std::vector<BYTE*> TEX;
		UINT twidth[2] = { 0 }, theight[2] = { 0 }, nmip = 0;

		UINT cnt = 0;
		for( int j = 0; j < CFG->CTP.size(); j++ ) {

			for( int i = 0; i < 4; i++ ) {
				
				sprintf( fname, "%s%s_%d_high_%d.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j, i );
				if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) )	{
					if( !AddTexture( TEX, data, twidth, theight, nmip ) )	break;
					else {
						CFG->CTP[j].midx = TEX.size() - i - 1;
						cnt++;
					}
				}
			}
		}

		//texture array 
		if( TEX.size() ) {
			//macro.
		/*	macro.Name = "LOW_DETAILS_MICROTEX_MULT";
			sprintf( line[13], "%0.1ff", CFG->TerrainLowDetailsMicroTex_mult );
			macro.Definition = line[13];
			SM.push_back( macro );*/

			memset( &tdesc, 0, sizeof(tdesc) );
			tdesc.ArraySize = TEX.size();
			tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
			tdesc.CPUAccessFlags = 0;
			tdesc.Format = DXGI_FORMAT_BC3_UNORM;
			tdesc.Height = twidth[0];
			tdesc.Width = theight[0];
			tdesc.MipLevels = nmip;
			tdesc.MiscFlags = 0;
			tdesc.SampleDesc.Count = 1;
			tdesc.Usage = D3D11_USAGE_DEFAULT;

			if( S_OK == Dev->CreateTexture2D( &tdesc, NULL, &tex2 ) ) {

				memset( &srvdesc, 0, sizeof(srvdesc) );
				srvdesc.Format = DXGI_FORMAT_BC3_UNORM;
				srvdesc.Texture2DArray.ArraySize = TEX.size();
				srvdesc.Texture2DArray.FirstArraySlice = 0;
				srvdesc.Texture2DArray.MipLevels = nmip;
				srvdesc.Texture2DArray.MostDetailedMip = 0;
				srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;

				if( S_OK == Dev->CreateShaderResourceView( tex2, &srvdesc, &aTextures_AverageHigh ) ) 
				{
					for( int j = 0; j < TEX.size(); j++ )
						iCtx->UpdateSubresource( tex2, j*nmip, NULL, TEX[j], twidth[0]/2, 0 );

					D3DX11FilterTexture( iCtx, tex2, 0, D3DX11_FILTER_LINEAR );
				}
				REL( tex2 );
			}

			

			//shaders for average detalization (billboards):
			::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\GS_Details_AverageHigh.cfg", &SM[0], NULL, "GS_Average", gs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
			ShowShaderCompilationError( hr, EBlob );
			HR( Dev->CreateGeometryShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &GS_Average ) );
			REL( SBlob );
			REL( EBlob );

			::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\PS_Details_AverageHigh.cfg", &SM[0], NULL, "PS_Average", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
			ShowShaderCompilationError( hr, EBlob );
			HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Average ) );
			REL( SBlob );
			REL( EBlob );

			//shaders for high detalization (ultra-lowpoly meshes)
			if( CFG->bTerrainDetails_High ) {

				::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\GS_Details_AverageHigh.cfg", &SM[0], NULL, "GS_High", gs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
				ShowShaderCompilationError( hr, EBlob );
				HR( Dev->CreateGeometryShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &GS_High ) );
				REL( SBlob );
				REL( EBlob );

				::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\PS_Details_AverageHigh.cfg", &SM[0], NULL, "PS_High", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
				ShowShaderCompilationError( hr, EBlob );
				HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_High ) );
				REL( SBlob );
				REL( EBlob );
			}
		}
	}

/*	//cities
	if( CFG->bTerrainDetails_Urban ) {

		//meshes with number of groups == number of buildings
		UINT idx = 0;
		FILE *file;
		ID3D11Buffer *VB;
		ID3D11Texture2D *tex;
		ID3D11ShaderResourceView *SRV;
		UINT nmip = 0;
		std::vector<BYTE*> TEX;
		UINT twidth[2] = { 0 }, theight[2] = { 0 };

		sprintf( fname, "%s%s_city_0.msh", "Meshes\\Terrain\\", term->objname );
		while( gc->TexturePath( fname, cpath ) && (file = fopen( cpath, "rb" )) ) {
			fclose( file );

			sprintf( fname, "%s%s_city_%d", "Terrain\\", term->objname, idx );
			MESHHANDLE hMesh = oapiLoadMesh( fname );
			UINT ngrp = oapiMeshGroupCount( hMesh );

			UINT nvtx = 0;
			TRIANGLE_VERTEX *data;
			for( int j = 0; j < ngrp; j++ ) {

				MESHGROUPEX *grp = oapiMeshGroupEx( hMesh, j );
				VERTEX_URBAN *ndata = new VERTEX_URBAN [nvtx + grp->nVtx];
				memset( ndata, 0, 20*(nvtx + grp->nVtx) );
				memcpy( ndata, data, nvtx*20 );
				delete [ ] data;
				data = ndata;
				ndata = data + nvtx;

				MATERIAL *mat = oapiMeshMaterial( hMesh, grp->MtrlIdx );
				for( int i = 0; i < grp->nVtx; i++ ) {
					ndata[i].PosL = D3DXVECTOR3( grp->Vtx[i].x, grp->Vtx[i].y, grp->Vtx[i].z );
					ndata[i].spower = mat->power;
					ndata[i].tidx = grp->TexIdx;

					BYTE *tdata;
					sprintf( fname, "%s%s_city_%d_group_%d.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j, i );
					if( gc->TexturePath( fname, cpath ) && ReadTexture( &tdata, cpath, twidth[0], theight[0] ) ) {
						if( !AddTextureUrban( TEX, tdata, twidth, theight, nmip ) ) {
							for( int i = 0; i < TEX.size(); i++ )	delete [ ] TEX[i];
							TEX.clear();
							goto skip;
						}
					}
				}
				nvtx++;				
			}

			D3D11_BUFFER_DESC bdesc;
			D3D11_SUBRESOURCE_DATA sdata;

			//triangle vertex buffer (=> geometry shader)
			memset( &bdesc, 0, sizeof(bdesc) );
			bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
			bdesc.ByteWidth = nvtx*20;
			bdesc.CPUAccessFlags = 0;
			bdesc.MiscFlags = 0;
			bdesc.StructureByteStride = 0;
			bdesc.Usage = D3D11_USAGE_IMMUTABLE;

			memset( &sdata, 0, sizeof(sdata) );
			sdata.pSysMem = data;

			Dev->CreateBuffer( &bdesc, NULL, &VB );
			VB_Urban.push_back( VB );

			//texture arrays (one per mesh)
			memset( &tdesc, 0, sizeof(tdesc) );
			tdesc.ArraySize = TEX.size();
			tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
			tdesc.CPUAccessFlags = 0;
			tdesc.Format = DXGI_FORMAT_BC1_UNORM;
			tdesc.Height = theight[0];
			tdesc.MipLevels = nmip;
			tdesc.MiscFlags = 0;
			tdesc.SampleDesc.Count = 1;
			tdesc.Usage = D3D11_USAGE_IMMUTABLE;
			tdesc.Width = twidth[0];

			Dev->CreateTexture2D( &tdesc, NULL, &tex );

			memset( &srvdesc, 0, sizeof(srvdesc) );
			srvdesc.Format = DXGI_FORMAT_BC1_UNORM;
			srvdesc.Texture2DArray.ArraySize = TEX.size();
			srvdesc.Texture2DArray.FirstArraySlice = 0;
			srvdesc.Texture2DArray.MipLevels = nmip;
			srvdesc.Texture2DArray.MostDetailedMip = 0;
			srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;

			Dev->CreateShaderResourceView( tex, &srvdesc, &SRV );

			for( int i = 0; i < oapiMeshTextureCount( hMesh ); i++ )
				iCtx->UpdateSubresource( tex, i*nmip, NULL, TEX[i], 0, 0 );
			D3DX11FilterTexture( iCtx, tex, 0, D3DX11_FILTER_LINEAR );
			aTextures_Urban.push_back( SRV );

			for( int i = 0; i < TEX.size(); i++ )	delete [ ] TEX[i];
			TEX.clear();
			oapiDeleteMesh( hMesh );

			idx++;
			sprintf( fname, "%s%s_city_%d.msh", "Meshes\\", term->objname, idx );
		}

		//shaders:
		::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\GS_Urban.cfg", &SM[0], NULL, "GS_Urban", gs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
		ShowShaderCompilationError( hr, EBlob );
		HR( Dev->CreateGeometryShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &GS_Urban ) );
		REL( SBlob );
		REL( EBlob );

		::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\PS_Urban.cfg", &SM[0], NULL, "PS_Urban", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
		ShowShaderCompilationError( hr, EBlob );
		HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Urban ) );
		REL( SBlob );
		REL( EBlob );

	}*/
skip:
	;
}

bool DetailsManager::AddTexture( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip ) {
	D3D11_SUBRESOURCE_DATA sdata;

	//check dimesions
	if( twidth[0] != twidth[1] || theight[0] != theight[1] ) {
		if( !twidth[1] && !theight[1] ) {
			//first array element
			twidth[1] = twidth[0];
			theight[1] = theight[0];
		}
		else {
			oapiWriteLog( "Error: dimensions of microtextures must be equal to each other." );
			for( int j = 0; j < CFG->CTP.size(); j++ )	CFG->CTP[j].flags = 0;
			for( int j = 0; j < TEX.size(); j++ )		delete [ ] TEX[j];
			TEX.clear();
			return false;
		}
	}

	TEX.push_back( data );

	if( !nmip )		GetMipMapNumber( twidth[0], theight[0], nmip );
	return true;
}

bool DetailsManager::AddTextureUrban( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip ) {
	D3D11_SUBRESOURCE_DATA sdata;

	//check dimesions
	if( twidth[0] != twidth[1] || theight[0] != theight[1] ) {
		if( !twidth[1] && !theight[1] ) {
			//first array element
			twidth[1] = twidth[0];
			theight[1] = theight[0];
		}
		else {
			oapiWriteLog( "Error: dimensions of microtextures must be equal to each other." );
			for( int j = 0; j < CFG->CTP.size(); j++ )	CFG->CTP[j].flags = 0;
			for( int j = 0; j < TEX.size(); j++ )		delete [ ] TEX[j];
			TEX.clear();
			return false;
		}
	}

	TEX.push_back( data );

	if( !nmip )		GetMipMapNumber( twidth[0], theight[0], nmip );
	return true;
}

/*
UINT
	DetailsManager::Stride_POINTVERTEX = 52,
	DetailsManager::Stride_ArgBuffer = 20,
	DetailsManager::Stride_MESHVERTEX = 44,
	DetailsManager::Stride_INSTANCEDATA = 24;
ID3D11InputLayout
	*DetailsManager::IL_POINTVERTEX = NULL,
	*DetailsManager::IL_MESHVERTEX = NULL;
ID3D11VertexShader
	*DetailsManager::VS_Average = NULL,
	*DetailsManager::VS_High = NULL;
ID3D11Buffer
	*DetailsManager::CB_AtmParams = NULL,
	*DetailsManager::CB_PlanetShadowParams = NULL,
	*DetailsManager::CB_LightingHigh = NULL,
	*DetailsManager::VB_Average = NULL,
	*DetailsManager::ArgumentBuffer = NULL,
	*DetailsManager::VB_InstanceData = NULL,
	*DetailsManager::VSCB_Average = NULL,
	*DetailsManager::CSCB_High = NULL;
ID3D11Resource
	*DetailsManager::aHeightMaps4,
	*DetailsManager::aLCoverMaps4,
	*DetailsManager::aTextures4;
ID3D11ShaderResourceView
	*DetailsManager::VB_AverageSRV = NULL,
	*DetailsManager::LCoverDistortionSRV = NULL,
	*DetailsManager::aTextures4SRV = NULL,
	*DetailsManager::aLCoverMaps4SRV = NULL,
	*DetailsManager::aHeightMaps4SRV = NULL;
ID3D11DepthStencilState
	*DetailsManager::DSS_Details = NULL;
ID3D11SamplerState
	*DetailsManager::SS_TileTexture = NULL,
	*DetailsManager::SS_BillBoardTexture = NULL,
	*DetailsManager::SS_MeshTexture = NULL;
ID3D11UnorderedAccessView
	*DetailsManager::ArgumentBufferUAV = NULL,
	*DetailsManager::InstanceDataUAV = NULL;

void DetailsManager::GlobalInit( ID3D11Buffer *_CB_AtmParams, ID3D11Buffer *_CB_PlanetShadowParams ) {

	DWORD j;
	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	CB_AtmParams = _CB_AtmParams;
	CB_PlanetShadowParams = _CB_PlanetShadowParams;
	CB_CTypePatams = NULL;

	D3D11_INPUT_ELEMENT_DESC iedesc[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 2, DXGI_FORMAT_R32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_Details.fx", NULL, NULL, "VS_Average", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateInputLayout( iedesc, 6, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_POINTVERTEX ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Average ) );
	REL( SBlob );
	REL( EBlob );

	D3D11_BUFFER_DESC bdesc;
	D3D11_SUBRESOURCE_DATA sdata;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	//=======================================================
	//		Vertex buffer of randomly positioned points
	//=======================================================

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_SHADER_RESOURCE;
	bdesc.ByteWidth = Stride_POINTVERTEX*NPOINT;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	bdesc.StructureByteStride = Stride_POINTVERTEX;
	bdesc.Usage = D3D11_USAGE_DEFAULT;
	
	double
		lat, lng,
		maxlat = PI05/512.0,
		maxlng = PI2/2048.0;
	POINTVERTEX vtx, *VTX = new POINTVERTEX [NPOINT];
	for( UINT j = 0; j < NPOINT; j++ ) {
		lat = maxlat*(double(rand())/double(RAND_MAX));
		lng = maxlng*(double(rand())/double(RAND_MAX));
		vtx.PosL.x = 1000.0*cos(lng)*cos(lat);
		vtx.PosL.y = 1000.0*sin(lat);
		vtx.PosL.z = 1000.0*sin(lng)*cos(lat);
		D3DXVec3Normalize( &vtx.NormL, &vtx.PosL );
		vtx.TangL.x = -1.0*sin(lng)*cos(lat);
		vtx.TangL.y = cos(lat);
		vtx.TangL.z = cos(lng)*cos(lat);
		vtx.TCrd = D3DXVECTOR2( lng/maxlng, lat/maxlat );
		vtx.dist = 0.0;//random ?
		vtx.rnd = double(rand())/double(RAND_MAX);
		VTX[j] = vtx;
	}

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = VTX;

	Dev->CreateBuffer( &bdesc, &sdata, &VB_Average );
	delete [ ] VTX;

	//samplers and dss
}

void DetailsManager::GlobalExit() {

	REL( IL_POINTVERTEX );
	REL( IL_MESHVERTEX );

	REL( VS_Average );
	REL( VS_High );

	REL( CB_LightingHigh );
	REL( VB_Average );
	REL( ArgumentBuffer );
	REL( VB_InstanceData );
	REL( VSCB_Average );

	REL( VB_AverageSRV );
	REL( LCoverDistortionSRV );

	REL( ArgumentBufferUAV );
	REL( InstanceDataUAV );

	REL( SS_TileTexture );
	REL( SS_BillBoardTexture );
	REL( SS_MeshTexture );

	REL( DSS_Details );
}

/*
DetailsManager::DetailsManager( TerrainManager *_term, PlanetaryConfig *_CFG ) {

	GS_Average = NULL;
	PS_Average = NULL;
	CS_High = NULL;
	PS_High = NULL;

	VB_MeshHigh = IB_MeshHigh = NULL;
	CB_CoverTypeParams = NULL;
	CB_MeshParams = NULL;

	MeshParamsSRV = NULL;
	aTexturesAverage = NULL;
	aTexturesHigh = NULL;

	term = _term;
	CFG = _CFG;
}

DetailsManager::~DetailsManager() {
	REL( GS_Average );
	REL( PS_Average );
	REL( CS_High );
	REL( PS_High );

	REL( MeshParamsSRV );
	REL( aTexturesAverage );
	REL( aTexturesHigh );

	REL( VB_MeshHigh );
	REL( IB_MeshHigh );
	REL( CB_CoverTypeParams );
	REL( CB_MeshParams );
}

void DetailsManager::Render( TILERENDERSPEC *TRS, UINT ntile ) {

	if( !CFG->bTerrainDetails_Average )		return;

	UINT j = 0;
	const UINT VBOffset = 0;

	//===================================================
	//					Average
	//===================================================

	//...
	iCtx->UpdateSubresource( cb_D3DXVECTOR4, 0, NULL, 0, 0, 0 );//!
	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, SC->GetVP(), 0, 0 );

	iCtx->IASetInputLayout( IL_POINTVERTEX );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );
	iCtx->IASetVertexBuffers( 0, 1, &VB_Average, &Stride_POINTVERTEX, &VBOffset );

	iCtx->VSSetShader( VS_Average, NULL, 0 );
	iCtx->VSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->VSSetConstantBuffers( 0, 1, &VSCB_Average );

	iCtx->GSSetShader( GS_Average, NULL, 0 );
	iCtx->GSSetSamplers( 0, 1, &SS_TileTexture );
//	if( CFG->DistortCoords )	iCtx->GSSetShaderResources( 1, 1, &LCoverDistortionSRV );	???
	iCtx->GSSetConstantBuffers( 0, 1, &cb_D3DXMATRIX_x1 );
	iCtx->GSSetConstantBuffers( 1, 1, &CB_CoverTypeParams );
	if( CFG->AtmosphereType == BASIC_ATMOSPHERE )	iCtx->GSSetConstantBuffers( 2, 1, &CB_AtmParams );

	iCtx->PSSetShader( PS_Average, NULL, 0 );
	iCtx->PSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_BillBoardTexture );
	iCtx->PSSetConstantBuffers( 0, 1, &cb_D3DXVECTOR4 );
	iCtx->PSSetConstantBuffers( 1, 1, &CB_CoverTypeParams );
	iCtx->PSSetShaderResources( 1, 1, &aTexturesAverage );

	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_Details, 0 );
	iCtx->RSSetState( RS_CullNone_Solid );

	VSCB_per_tile vbuf;
	for( j = 0; j < ntile; j++ ) {

		vbuf.W = TRS[j].mWorld;
		vbuf.flags = 0;

		vbuf.TexOff[0] = TRS[j].RANGE.range_tex.tumax - TRS[j].RANGE.range_tex.tumin;
		vbuf.TexOff[1] = TRS[j].RANGE.range_tex.tumin;
		vbuf.TexOff[2] = TRS[j].RANGE.range_tex.tvmax - TRS[j].RANGE.range_tex.tvmin;
		vbuf.TexOff[3] = TRS[j].RANGE.range_tex.tvmin;

		if( TRS[j].Tex.htex ) {
			iCtx->VSSetShaderResources( 0, 1, &TRS[j].Tex.htex );

			vbuf.flags = 2;
			vbuf.TexOff_height[0] = TRS[j].RANGE.range_height.tumax - TRS[j].RANGE.range_height.tumin;
			vbuf.TexOff_height[1] = TRS[j].RANGE.range_height.tumin;
			vbuf.TexOff_height[2] = TRS[j].RANGE.range_height.tvmax - TRS[j].RANGE.range_height.tvmin;
			vbuf.TexOff_height[3] = TRS[j].RANGE.range_height.tvmin;
		}

		if( TRS[j].Tex.ctex ) {
			iCtx->GSSetShaderResources( 0, 1, &TRS[j].Tex.ctex );

			vbuf.TexOff_lcover[0] = TRS[j].RANGE.range_lcover.tumax - TRS[j].RANGE.range_lcover.tumin;
			vbuf.TexOff_lcover[1] = TRS[j].RANGE.range_lcover.tumin;
			vbuf.TexOff_lcover[2] = TRS[j].RANGE.range_lcover.tvmax - TRS[j].RANGE.range_lcover.tvmin;
			vbuf.TexOff_lcover[3] = TRS[j].RANGE.range_lcover.tvmin;

			iCtx->UpdateSubresource( VSCB_Average, 0, NULL, &vbuf, 0, 0 );
			iCtx->Draw( NPOINT, 0 );
		}
	}
	iCtx->GSSetShader( NULL, NULL, 0 );

	//===================================================
	//		High details - computing indirect args
	//===================================================

	if( !CFG->bTerrainDetails_High )	return;

/*	//fill texture arrays...
	static TILERENDERSPEC *TRS4[4] = { NULL }, *TRS4_old[4] = { NULL };
	static double td_adist[4] = { 0.0 };

	memset( TRS4, 0, sizeof(TILERENDERSPEC*)*4 );
	memset( td_adist, 0, 8*4 );
	double cam_lng, cam_lat, cam_rad, tile_lng, tile_lat, tile_rad;
	oapiGlobalToEqu( term->obj, SC->CamPos, &cam_lng, &cam_lat, &cam_rad );

	//find 4 nearest tiles:
	for( UINT j = 0; j < ntile; j++ ) {
		tile_lng = PI2/double(TRS[j].nlng);
		tile_lng = PI2*(double(TRS[j].ilng)/double(TRS[j].nlng)) + tile_lng/2.0;

		tile_lat = PI05/double(TRS[j].nlat);
		tile_lat = PI05*(double(TRS[j].ilat)/double(TRS[j].nlat)) + tile_lat/2.0;

		double dlng = abs( tile_lng - cam_lng );
		double dlat = abs( tile_lat - cam_lat );
		double adist = sqrt( dlng*dlng + dlat*dlat );

		if( adist > td_adist[0] ) {
			TRS4[0] = &TRS[j];
			td_adist[0] = adist;
		}
		else if( adist > td_adist[1] ) {
			TRS4[1] = &TRS[j];
			td_adist[1] = adist;
		}
		else if( adist > td_adist[2] ) {
			TRS4[2] = &TRS[j];
			td_adist[2] = adist;
		}
		else if( adist > td_adist[3] ) {
			TRS4[3] = &TRS[j];
			td_adist[3] = adist;
		}
	}

	//copy texture and world matrix data:
	CSCB_High_per_frame cbuf;
	cbuf.V = *SC->GetV();

	float ap = (float)SC->GetCamAperture();
	cbuf.h = tan( ap );
	cbuf.dfr = 0.0015f*cbuf.h;
	cbuf.hf = 1.0f/cos( ap );
	float as = cfg->Aspect;
	cbuf.w = cbuf.h*as;
	cbuf.wf = cbuf.hf*as;
	cbuf.ntile = 4;

	ID3D11Resource *res;
	for( UINT j = 0; j < 4; j++ ) {
		if( TRS4[j] != TRS4_old[j] ) {
			cbuf.W[j] = TRS4[j]->mWorld;

			TRS4[j]->Tex.htex->GetResource( &res );
			iCtx->CopySubresourceRegion( aHeightMaps4, j, 0, 0, 0, res, 0, NULL );

			TRS4[j]->Tex.ctex->GetResource( &res );
			iCtx->CopySubresourceRegion( aLCoverMaps4, j, 0, 0, 0, res, 0, NULL );

			TRS4[j]->Tex.tex->GetResource( &res );
			iCtx->CopySubresourceRegion( aTextures4, j, 0, 0, 0, res, 0, NULL );
		}
	}

	memcpy( TRS4_old, TRS4, sizeof(TILERENDERSPEC*)*4 );

	iCtx->UpdateSubresource( CSCB_High, 0, NULL, &cbuf, 0, 0 );

	const UINT UAVInitCount = 0;

	iCtx->CSSetShader( CS_High, NULL, 0 );
	iCtx->CSSetConstantBuffers( 0, 1, &CSCB_High );
	iCtx->CSSetConstantBuffers( 1, 1, &CB_MeshParams );
	iCtx->CSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->CSSetShaderResources( 0, 1, &aHeightMaps4SRV );
	iCtx->CSSetShaderResources( 1, 1, &aLCoverMaps4SRV );
	iCtx->CSSetShaderResources( 2, 1, &VB_AverageSRV );
	iCtx->CSSetUnorderedAccessViews( 0, 1, &ArgumentBufferUAV, &UAVInitCount );
	iCtx->CSSetUnorderedAccessViews( 1, 1, &InstanceDataUAV, &UAVInitCount );

	iCtx->Dispatch( 1, NPOINT/64, 1 );
	iCtx->CSSetShader( NULL, NULL, 0 );

	//===================================================
	//		High details - indirect instanced draw
	//===================================================

	//...
	//...
	iCtx->UpdateSubresource( CB_LightingHigh, 0, NULL, 0, 0, 0 );//!
	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, SC->GetVP(), 0, 0 );
	iCtx->UpdateSubresource( cb_D3DXVECTOR4, 0, NULL, 0, 0, 0 );//!

	iCtx->IASetInputLayout( IL_MESHVERTEX );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->IASetVertexBuffers( 0, 1, &VB_MeshHigh, &Stride_MESHVERTEX, &VBOffset );
	iCtx->IASetVertexBuffers( 1, 1, &VB_InstanceData, &Stride_INSTANCEDATA, &VBOffset );

	iCtx->VSSetShader( VS_High, NULL, 0 );
	iCtx->VSSetConstantBuffers( 0, 1, &cb_D3DXMATRIX_x1 );

	iCtx->PSSetShader( PS_High, NULL, 0 );
	iCtx->PSSetConstantBuffers( 0, 1, &cb_D3DXVECTOR4 );
	iCtx->PSSetConstantBuffers( 1, 1, &CB_LightingHigh );
	iCtx->PSSetConstantBuffers( 2, 1, &CB_MeshParams );
	iCtx->PSSetShaderResources( 0, 1, &aTexturesHigh );
	iCtx->PSSetShaderResources( 1, 1, &aTextures4SRV );
	iCtx->PSSetSamplers( 0, 1, &SS_MeshTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_TileTexture );

	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_Details, 0 );
	iCtx->RSSetState( RS_CullNone_Solid );

	for( UINT j = 0; j < MAX_INDIRECT_TYPE; j++ )
		iCtx->DrawIndexedInstancedIndirect( ArgumentBuffer, j*Stride_ArgBuffer );*/
//}

/*
Earth_micro_disp_flat.dds		A8
Earth_micro_disp_coarse.dds	`	A8
Earth_micro_flat.dds			dxt1
Earth_micro_coarse.dds			dxt1
Earth_micro_norm_flat.dds		dxt1
Earth_micro_norm_coarse.dds		dxt1

from:
Earth_low_0.dds			dxt1	Earth_low_0_norm.dds
Earth_average_0.dds		dxt5	Earth_average_0_norm.dds
Earth_high_0.dds		dxt5
Earth_high_0.msh

to:
Earth_low_10.dds		dxt1	Earth_low_0_norm.dds
Earth_average_10.dds	dxt5	Earth_average_0_norm.dds
Earth_high_10.dds		dxt5
Earth_high_10.msh
*/





/*








	if( !CFG->bTerrainDetails_Average )	return;

	char fname[256], cpath[256];
	BYTE *data = NULL;
	UINT twidth[2] = { 0 }, theight[2] = { 0 }, nmip = 0;
	D3D11_TEXTURE2D_DESC tdesc;
	D3D11_SUBRESOURCE_DATA sdata;
	std::vector<D3D11_SUBRESOURCE_DATA> SRDATA;
	std::vector<BYTE*> TEX;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	ID3D11Texture2D *tex;
	D3D11_BUFFER_DESC bdesc;

	std::vector<D3D10_SHADER_MACRO> SM;
	D3D10_SHADER_MACRO macro;

	//==============================
	//		Average - Diffuse
	//==============================
/*	for( int j = 0; j < CFG->CTP.size(); j++ ) {

		sprintf( fname, "%s%s_average_%d.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j );
		if( !gc->TexturePath( fname, cpath ) || !ReadTexture( &data, cpath, twidth[0], theight[0] ) )	continue;
		else if( !AddTexture( TEX, data, twidth, theight, nmip ) )	goto skip;

		CFG->CTP[j].tidx = j;
	}

	for( int j = 0; j < CFG->CTP.size(); j++ ) {

		sprintf( fname, "%s%s_average_%d_norm.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j );
		if( !gc->TexturePath( fname, cpath ) || !ReadTexture( &data, cpath, twidth[0], theight[0] ) )	continue;
		else if( !AddTexture( TEX, data, twidth, theight, nmip ) )	goto skip;

		CFG->CTP[j].nidx = j;
	}*/
/*
	if( TEX.size() ) {

		memset( &tdesc, 0, sizeof(tdesc) );
		tdesc.ArraySize = TEX.size();
		tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		tdesc.CPUAccessFlags = 0;
		tdesc.Format = DXGI_FORMAT_BC3_UNORM;
		tdesc.Height = theight[0];
		tdesc.Width = twidth[0];
		tdesc.MipLevels = nmip;
		tdesc.MiscFlags = 0;
		tdesc.SampleDesc.Count = 1;
		tdesc.Usage = D3D11_USAGE_DEFAULT;

		if( S_OK == Dev->CreateTexture2D( &tdesc, NULL, &tex ) ) {

			memset( &srvdesc, 0, sizeof(srvdesc) );
			srvdesc.Format = DXGI_FORMAT_BC3_UNORM;
			srvdesc.Texture2DArray.ArraySize = TEX.size();
			srvdesc.Texture2DArray.FirstArraySlice = 0;
			srvdesc.Texture2DArray.MipLevels = nmip;
			srvdesc.Texture2DArray.MostDetailedMip = 0;
			srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;

			if( S_OK == Dev->CreateShaderResourceView( tex, &srvdesc, &aTexturesAverage ) ) {
				for( int j = 0; j < TEX.size(); j++ )
					iCtx->UpdateSubresource( tex, j*nmip, NULL, TEX[j], twidth[0], 0 );
				D3DX11FilterTexture( iCtx, tex, 0, D3DX11_FILTER_LINEAR );
			}
		}

		for( int j = 0; j < TEX.size(); j++ )	delete [ ] TEX[j];
		TEX.clear();
	}

skip:

	switch( CFG->AtmosphereType ) {
	case NO_ATMOSPHERE:
		macro.Name = "NO_ATMOSPHERE";
		break;
	case BASIC_ATMOSPHERE:
		macro.Name = "BASIC_ATMOSPHERE";
		break;
	case PAS_ATMOSPHERE:
		macro.Name = "PAS_ATMOSPHERE";
		break;
	}
	macro.Definition = "1";
	SM.push_back( macro );

	//terrain shadows:
	if( CFG->bTerrainShadows ) {
		macro.Name = "TERRAIN_SHADOWS";
		macro.Definition = "1";
		SM.push_back( macro );
	}

	//planetary shadows:
	if( CFG->bPlanetaryShadows ) {
		macro.Name = "PLANETARY_SHADOWS";
		macro.Definition = "1";
		SM.push_back( macro );
	}

	//ring shadows:
	if( CFG->bRingShadows ) {
		macro.Name = "RING_SHADOWS";
		macro.Definition = "1";
		SM.push_back( macro );
	}

	char line[32];
	macro.Name = "MAXCTYPE";
	sprintf( line, "%d", CFG->CTP.size() );
	macro.Definition = line;
	SM.push_back( macro );

	macro.Name = NULL;
	macro.Definition = NULL;
	SM.push_back( macro );

	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\GS_Details_Average.cfg", &SM[0], NULL, "GS_Average", gs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateGeometryShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &GS_Average ) );
	REL( SBlob );
	REL( EBlob );

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\PS_Details_Average.cfg", &SM[0], NULL, "PS_Average", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Average ) );
	REL( SBlob );
	REL( EBlob );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = CFG->CTP.size()*32;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = &CFG->CTP[0];
	
	Dev->CreateBuffer( &bdesc, &sdata, &CB_CoverTypeParams );

	//==========================================
	//					High
	//==========================================

	if( !CFG->bTerrainDetails_High )	return;*/
/*
	//meshes:
	MESHPARAM mp;
	std::vector<MESHHANDLE> MSH;
	std::vector<MESHPARAM> MP;
	std::vector<MESHGROUPEX*> MGEx;
	UINT nmsh = 0, ngrp = 0, sidx = 0, svtx = 0;
	MESHHANDLE hMesh = NULL;
	dim = ntex = j = 0;

	while( hMesh ) {
		sprintf( fname, "Terrain\\%s_high_%d", term->objname, nmsh );
		hMesh = oapiLoadMesh( fname );
		ngrp = oapiMeshGroupCount( hMesh );
		sprintf( fname, "Terrain\\%s_high_%d.dds", term->objname, nmsh );
		if( ngrp == 1 && gc->TexturePath( fname, cpath ) && ReadTexture( &data, fname, dim ) ) {
			MESHGROUPEX *grp = oapiMeshGroupEx( hMesh, 0 );
			MGEx.push_back( grp );
			mp.nidx = grp->nIdx;
			mp.nvtx = grp->nVtx;
			mp.sidx = sidx;
			mp.svtx = svtx;
			sidx += grp->nIdx;
			svtx += grp->nVtx;
			MP.push_back( mp );
			MSH.push_back( hMesh );

			//diffuse texture SR data:
			TEX.push_back( data );
			sdata.pSysMem = data;
			sdata.SysMemPitch = dim/2;
			SRDATA.push_back( sdata );
			if( nmsh < CFG->CTP.size() )	CFG->CTP[nmsh].hidx = nmsh;

			if( SRDATA.size() == 1 )
				nmip = GetTextureMipMapCount( dim );

			memset( &sdata, 0, sizeof(sdata) );
			for( j = 0; j < nmip; j++ )		SRDATA.push_back( sdata );

			sprintf( fname, "Terrain\\%s_high_%d_norm.dds", term->objname, nmsh );
			if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, fname, dim ) ) {
				NTEX.push_back( data );
			}
			ntex++;
		}
		else	hMesh = NULL;
		nmsh++;
	}

	if( MSH.size() ) {

		//mesh vertex/index data:
		NTVERTEX *Vtx = new NTVERTEX [svtx], *vtx;
		vtx = Vtx;
		for( j = 0; j < nmsh; j++ ) {
			memcpy( vtx, MGEx[j]->Vtx, MP[j].nvtx );
			vtx += MP[j].nvtx;
		}

		WORD *Idx = new WORD [sidx], *idx;
		idx = Idx;
		for( j = 0; j < nmsh; j++ ) {
			memcpy( idx, MGEx[j]->Idx, MP[j].nidx );
			idx += MP[j].nidx;
		}

		memset( &bdesc, 0, sizeof(bdesc) );
		bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		bdesc.ByteWidth = 32*svtx;
		bdesc.CPUAccessFlags = 0;
		bdesc.MiscFlags = 0;
		bdesc.StructureByteStride = 0;
		bdesc.Usage = D3D11_USAGE_IMMUTABLE;

		memset( &sdata, 0, sizeof(sdata) );
		sdata.pSysMem = Vtx;

		Dev->CreateBuffer( &bdesc, &sdata, &VB_MeshHigh );

		memset( &bdesc, 0, sizeof(bdesc) );
		bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
		bdesc.ByteWidth = 2*sidx;
		bdesc.CPUAccessFlags = 0;
		bdesc.MiscFlags = 0;
		bdesc.StructureByteStride = 0;
		bdesc.Usage = D3D11_USAGE_IMMUTABLE;

		memset( &sdata, 0, sizeof(sdata) );
		sdata.pSysMem = Idx;

		Dev->CreateBuffer( &bdesc, &sdata, &IB_MeshHigh );

		for( j = 0; j < nmsh; j++ )
			oapiDeleteMesh( MSH[j] );
		MGEx.clear();
		MSH.clear();

		//mesh params buffer:
		memset( &bdesc, 0, sizeof(bdesc) );
		bdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		bdesc.ByteWidth = 16*MP.size();
		bdesc.CPUAccessFlags = 0;
		bdesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
		bdesc.StructureByteStride = 16;
		bdesc.Usage = D3D11_USAGE_IMMUTABLE;

		memset( &sdata, 0, sizeof(sdata) );
		sdata.pSysMem = &MP[0];

	//	Dev->CreateBuffer( &bdesc, &sdata, &MeshParams );

		memset( &srvdesc, 0, sizeof(srvdesc) );
		srvdesc.Format = DXGI_FORMAT_UNKNOWN;
		srvdesc.Buffer.ElementOffset = 0;
		srvdesc.Buffer.ElementWidth = 16;
		srvdesc.Buffer.FirstElement = 0;
		srvdesc.Buffer.NumElements = MP.size();

	//	Dev->CreateShaderResourceView( MeshParams, &srvdesc, &MeshParamsSRV );
		MP.clear();

		//textures:
		//add normal maps if they were loaded correctly:
		if( TEX.size() == NTEX.size() ) {
			for( j = 0; j < NTEX.size(); j++ ) {
				sdata.pSysMem = NTEX[j];
				sdata.SysMemPitch = dim/2;
				SRDATA.push_back( sdata );
				memset( &sdata, 0, sizeof(sdata) );
				for( j = 0; j < nmip; j++ )
					SRDATA.push_back( sdata );
				ntex++;
			}
		}

		memset( &tdesc, 0, sizeof(tdesc) );
		tdesc.ArraySize = ntex;
		tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		tdesc.CPUAccessFlags = 0;
		tdesc.Format = DXGI_FORMAT_BC3_UNORM;
		tdesc.Height = dim;
		tdesc.MipLevels = nmip;
		tdesc.MiscFlags = 0;
		tdesc.SampleDesc.Count = 1;
		tdesc.Usage = D3D11_USAGE_IMMUTABLE;
		tdesc.Width = dim;

		Dev->CreateTexture2D( &tdesc, &SRDATA[0], &tex );

		memset( &srvdesc, 0, sizeof(srvdesc) );
		srvdesc.Format = DXGI_FORMAT_BC3_UNORM;
		srvdesc.Texture2DArray.ArraySize = TEX.size() + NTEX.size();
		srvdesc.Texture2DArray.FirstArraySlice = 0;
		srvdesc.Texture2DArray.MipLevels = nmip;
		srvdesc.Texture2DArray.MostDetailedMip = 0;
		srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;

		Dev->CreateShaderResourceView( tex, &srvdesc, &aTexturesHigh );
		SRDATA.clear();

		//cleanup:
		for( j = 0; j < TEX.size(); j++ )
			delete [ ] TEX[j];
		TEX.clear();

		if( NTEX.size() ) {
			for( j = 0; j< NTEX.size(); j++ )
				delete [ ] NTEX[j];
			NTEX.clear();
		}

		delete [ ] Vtx;
		delete [ ] Idx;
	}*/
//}